﻿using UnityEngine;
using System;
using System.Linq;
using System.Reflection;
using System.Collections;
using System.Collections.Generic;

namespace CxExtension
{
	public static class TypeUtility
	{
		private static Assembly[] assembliesLookup;
		private static Dictionary<string,Type> typeLookup;
		private static Dictionary<Type,Type[]> subTypesMap;
		static TypeUtility()
		{
			assembliesLookup = AppDomain.CurrentDomain.GetAssemblies();
			// Remove Editor assemblies
			var runtimeAsms = new List<Assembly>();
			foreach(Assembly asm in assembliesLookup)
			{
				if(!asm.GetName().Name.Contains("Editor"))
					runtimeAsms.Add(asm);
			}
			assembliesLookup = runtimeAsms.ToArray();
			typeLookup = new Dictionary<string,Type>();
			subTypesMap = new Dictionary<Type,Type[]>();
		}
		/// <summary>
		/// 获取所有派生类的名字数组
		/// </summary>
		/// <param name="self"></param>
		/// <returns></returns>
		public static string[] GetSubTypeNames(this Type self)
		{
			return self.GetSubTypes().Select(x => x.Name).ToArray();
		}
		/// <summary>
		/// 获取某个名字的派生类
		/// </summary>
		/// <param name="self"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		public static Type GetSubTypeByName(this Type self,string name)
		{
			var types = self.GetSubTypes();
			var type = types.FindA(item => item.Name == name);
			if(type == null)
			{
				Debug.LogWarningFormat("not find the sub class[{0}] in [{1}]",name,self.Name);
			}
			return type;
		}
		/// <summary>
		/// 取得某个名字的派生类实例
		/// </summary>
		/// <param name="self"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		public static object GetSubInstanceByName(this Type self,string name)
		{
			var tp = self.GetSubTypeByName(name);
			object v = null;
			if(tp != null)
			{
				v = Activator.CreateInstance(tp);
			}
			return v;
		}
		/// <summary>
		/// 取得所有派生类型
		/// </summary>
		/// <param name="self"></param>
		/// <returns></returns>
		public static Type[] GetSubTypes(this Type self)
		{
			Type[] types;
			if(!subTypesMap.TryGetValue(self,out types))
			{
				IEnumerable<Type> typesI = AppDomain.CurrentDomain.GetAssemblies().SelectMany(assembly => assembly.GetTypes()).Where(type => type.IsSubclassOf(self));
				types = typesI.ToArray();
				subTypesMap[self] = types;
			}
			return types;
		}
		/// <summary>
		/// 获取指定名字的成员的类型
		/// </summary>
		/// <param name="self"></param>
		/// <param name="name"></param>
		/// <returns></returns>
		public static Type GetMemberType(this Type self,string name)
		{
			FieldInfo fieldInfo = self.GetField(name);
			if(fieldInfo != null)
			{
				return fieldInfo.FieldType;
			}
			PropertyInfo propertyInfo = self.GetProperty(name);
			if(propertyInfo != null)
			{
				return propertyInfo.PropertyType;
			}
			return null;
		}
		/// <summary>
		/// 取得某个名字的类型
		/// </summary>
		/// <param name="name"></param>
		/// <returns></returns>
		public static Type GetType(string name)
		{
			/*	Type type = null;
				if (typeLookup.TryGetValue (name, out type)) {
					return type;
				}
				type =Type.GetType(name) ?? Type.GetType("UnityEngine."+name+",UnityEngine") ?? Type.GetType (name + ",Assembly-CSharp-firstpass") ?? Type.GetType (name + ",Assembly-CSharp");
				if (type == null) {
					foreach (Assembly asm in TypeUtility.assembliesLookup) {
						type = asm.GetType (name);
						if (type != null)
							break;
					}
				}

				typeLookup.Add(name, type);
				return type;*/

			Type type = null;
			if(typeLookup.TryGetValue(name,out type))
			{
				return type;
			}

			foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies())
			{
				Type[] assemblyTypes = a.GetTypes();
				for(int j = 0;j < assemblyTypes.Length;j++)
				{
					if(assemblyTypes[j].Name == name)
					{
						typeLookup.Add(name,assemblyTypes[j]);
						return assemblyTypes[j];
					}
				}
			}
			return null;
		}

		public static Type[] GetTypeByName(string className)
		{
			List<Type> returnVal = new List<Type>();

			foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies())
			{
				Type[] assemblyTypes = a.GetTypes();
				for(int j = 0;j < assemblyTypes.Length;j++)
				{
					if(assemblyTypes[j].Name == className)
					{
						returnVal.Add(assemblyTypes[j]);
					}
				}
			}

			return returnVal.ToArray();
		}
	}
}